/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package im.clusterhead;

import im.general.AgentLogBehaviour;
import im.general.BaseSuperAgent;
import im.general.Config;
import im.node.MobileNodeInfo;
import im.resources.ResourceStatus;
import jade.core.AID;
import jade.lang.acl.ACLMessage;
import jade.wrapper.ControllerException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author bluejack/Gnaskky
 */
public class ClusterHeadAgent extends BaseSuperAgent {

    protected int cloneCount;
    public String myMNguestAgent;
    protected String clonedMNguestAgent;
    protected UpdateStatusListenBehaviour updateStatusListenBehaviour = null;
    //ClusterHeadLogBehaviour logBehaviour=null;
    

    protected Double clonedCH_idx;

    public Double getClonedCH_idx() {
        return clonedCH_idx;
    }

    public void setClonedCH_idx(Double clonedCH_idx) {
        this.clonedCH_idx = clonedCH_idx;
    }

    String nodePromoted;

    public String getNodePromoted() {
        return nodePromoted;
    }

    public void setNodePromoted(String nodePromoted) {
        this.nodePromoted = nodePromoted;
    }

    public ClusterHeadAgent(){
        cloneCount=0;
        myMNguestAgent = Config.EVENT_SERVICE_NAME;
    }

    @Override
    public void setup() {
        super.setup();
        addBehaviour(new ClusterJoinServerBehaviour(this));
        addBehaviour(new ClusterUnjoinServerBehaviour(this));
        //addBehaviour(new PublishListenBehaviour(this));
        addBehaviour(new NotifyListenBehaviour(this));
        addBehaviour(new SubscribeListenBehaviour(this));
        addBehaviour(new UpdateStatusListenBehaviour(this));
        //addBehaviour(new ClusterHeadListenBehaviour(this));
        
    }

    public boolean checkClusterizeConditions() {
        /* will use Config.MAX_CLUSTER_SIZE */
        return this.getNodes().size() > Config.MAX_CLUSTER_SIZE;
    }

    public boolean checkShrinkConditions() {
        /* will use Config.MAX_CLUSTER_SIZE */
        return this.getNodes().size() < Config.MIN_CLUSTER_SIZE;
    }

    public Double getNodeIdx(String mn) {
        return new Double(this.getNodes().get(mn).getResourceStatus().getStatusIndex());
    }

    public MobileNodeInfo selectNodeToMakeCH() {
        //TODO improve selection policy
            if( Config.CLUSTER_POLICY == Config.CLUSTER_HEURISTIC ) {
            Enumeration ks = getNodes().keys();
            double best_idx = -1.0;
            double curr_idx = -1.0;
            String best_mn = null, curr_mn;

            while(ks.hasMoreElements()){
                curr_mn = (String) ks.nextElement();
                //System.out.println("DBG -> " + (MobileNodeInfo)nodes.get(curr_mn) + " - ");
                //System.out.println("DBG2 -> " + ((MobileNodeInfo)nodes.get(curr_mn) ).getResourceStatus() );
                curr_idx = ( (MobileNodeInfo)getNodes().get(curr_mn) ).getResourceStatus().getStatusIndex();
                if( curr_idx > best_idx && !curr_mn.equals(this.myMNguestAgent) ) {
                    best_idx = curr_idx;
                    best_mn = curr_mn;
                }
            }
            return (MobileNodeInfo)getNodes().get(best_mn);
        }

        return (MobileNodeInfo) getNodes().elements().nextElement();

    }

    @Override
    protected void afterMove() {
        removeBehaviour(logBehaviour);
            logBehaviour=new AgentLogBehaviour(this);
            addBehaviour(logBehaviour);
            
            // Advertise cluster MNs
            ACLMessage adv_ch_msg = new ACLMessage(ACLMessage.PROPAGATE);
            adv_ch_msg.addUserDefinedParameter(Config.IM_MSG_TYPE, Config.NEW_CH_ADVERTISE_MSG);
            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_NAME, this.getLocalName());
            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_IDX, this.getClonedCH_idx().toString());
        try {
            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_CONTAINER, this.getContainerController().getContainerName());
        } catch (ControllerException ex) {
            Logger.getLogger(ClusterHeadAgent.class.getName()).log(Level.SEVERE, null, ex);
        }

            Enumeration nod = this.getNodeNames();

            String mn_n;


            adv_ch_msg.addReceiver(new AID(this.myMNguestAgent, AID.ISGUID));

            this.nodePromoted = null;

            //this.add
            while(nod.hasMoreElements()) {
                mn_n = (String)nod.nextElement();
                if( !mn_n.equals(this.myMNguestAgent)){
                System.out.println(this.getLocalName() + " push to: " + mn_n);
                adv_ch_msg.addReceiver(new AID(mn_n, AID.ISGUID));
                }
            }

            this.sendMessage(adv_ch_msg);

            this.doActivate();
    }

    @Override
    protected void afterClone() {
        try {
            removeBehaviour(logBehaviour);
            logBehaviour=new AgentLogBehaviour(this);
            addBehaviour(logBehaviour);
            
            System.out.println("[ClusterHeadAgent   ]\tNew ClusterHead Raised:\t  "
                    + this.getLocalName() + "\t" + this.getContainerController().getContainerName() + "\t" +  this.clonedMNguestAgent );

            this.myMNguestAgent = this.clonedMNguestAgent;
            this.clonedMNguestAgent = null;

            // Advertise cluster MNs
            ACLMessage adv_ch_msg = new ACLMessage(ACLMessage.PROPAGATE);
            adv_ch_msg.addUserDefinedParameter(Config.IM_MSG_TYPE, Config.NEW_CH_ADVERTISE_MSG);
            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_NAME, this.getLocalName());
            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_IDX, this.getClonedCH_idx().toString());

            adv_ch_msg.addUserDefinedParameter(Config.NEW_CH_CONTAINER,  this.getContainerController().getContainerName() );

            Enumeration nod = this.getNodeNames();

            String mn_n;


            adv_ch_msg.addReceiver(new AID(this.nodePromoted, AID.ISGUID));

            this.nodePromoted = null;

            //this.add
            while(nod.hasMoreElements()) {
                mn_n = (String)nod.nextElement();
                System.out.println(this.getLocalName() + " push to: " + mn_n);
                adv_ch_msg.addReceiver(new AID(mn_n, AID.ISGUID));
            }

            this.sendMessage(adv_ch_msg);
            
            getNodes().clear();
            cloneCount = 0;

            this.doActivate();
            
        } catch (Exception ex) {
            Logger.getLogger(ClusterHeadAgent.class.getName()).log(Level.SEVERE, null, ex);
            ex.printStackTrace();
        }
    }

    @Override
    protected void takeDown() {
        try {
            System.out.println("[ClusterHeadAgent   ]\tClusterHead Shrinking ("+ this.getNodes().size() +")\t"
                    + this.getLocalName() + "\t" + this.getContainerController().getContainerName());
        } catch (ControllerException ex) {
            Logger.getLogger(ClusterHeadAgent.class.getName()).log(Level.SEVERE, null, ex);
            ex.printStackTrace();
        }
    }

    public int getCloneCount() {
        return cloneCount;
    }

    public void addCloneCount() {
        cloneCount++;
    }

    @Override
    public void removeNode(String name) {
         super.removeNode(name);
        advertiseResources();
    }

    @Override
    public void addNode(String name, MobileNodeInfo info) {
        super.addNode(name, info);
        advertiseResources();
    }

    private void advertiseResources() {
        try {
            if( this.getAID().getLocalName().equals(Config.CH_ROOT_NAME))
                return;

            // updates resources at mn agent
            ACLMessage upd_res_msg = new ACLMessage(ACLMessage.INFORM_REF);
            upd_res_msg.addUserDefinedParameter(Config.IM_RMSG_TYPE, Config.IM_RMSG_TYPE);
            upd_res_msg.setContentObject(new Integer(getNodesList().size()));
            upd_res_msg.addReceiver(new AID(this.myMNguestAgent, AID.ISGUID));
            this.sendMessage(upd_res_msg);
        } catch (IOException ex) {
            Logger.getLogger(ClusterHeadAgent.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public ResourceStatus getStatus() {
        if( this.myMNguestAgent.equals(Config.EVENT_SERVICE_NAME))
            return null;
        if( this.getNodeInfo(this.myMNguestAgent) == null )
            return null;

        return this.getNodeInfo(this.myMNguestAgent).getResourceStatus();
    }

    public int getClusterSize() {
        return this.getNodes().size();
    }

    public Vector getNodesList() {
        return new Vector(getNodes().keySet());
    }

    public String getClonedMNguestAgent() {
        return clonedMNguestAgent;
    }

    public void setClonedMNguestAgent(String clonedMNguestAgent) {
        this.clonedMNguestAgent = clonedMNguestAgent;
    }

    @Override
    public boolean updateResources() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
    
}
